/* Zatok - язык программирования текстовых игр
 * (С) 2010 - 2012 Меркулов Матвей Юрьевич
 * Код лицензирован под лицензией GNU LGPL
 * Вы можете прочитать текст лицензии по адресу:
 * http://www.gnu.org/copyleft/lesser.html
 * или прочитать русский перевод по адресу:
 * http://ru.wikisource.org/wiki/GNU_Lesser_General_Public_License
 */

package base;

import base.context.CCommand;
import base.context.CContext;
import base.intrface.CChoice;
import base.intrface.CCommandText;
import base.intrface.CMenu;
import base.intrface.CMenuItem;
import base.module.CEquate;
import base.module.CModule;
import base.userfunction.CFunctionCall;
import base.userfunction.CUserFunctionID;
import base.value.CNo;
import base.value.CValue;
import base.value.userobject.CNull;
import base.value.userobject.CUserObject;
import java.io.File;
import java.io.FileFilter;
import java.util.LinkedList;
import java.util.ListIterator;
import parser.CParser;




public class CProject {
	public static final String Version = "0.10.2";
	public static String Name;
	public static CUserObject RootObject;
	public static CFunctionCall Actions;
	public static CFunctionCall RepeatingBlock;
	public static CFunctionCall WhenWindowSizeIsChanged;
	public static LinkedList<CUserObject> ObjectsList = new LinkedList<CUserObject>();
	public static LinkedList<CModule> Commands = new LinkedList<CModule>();
	public static CUserObject CurrentObject;
	public static CFunctionCall CurrentFunctionCall;
	public static CContext CurrentContext;
	public static CChoice CurrentChoice;
	public static ListIterator<CUserObject> CurrentObjectIterator = null;
	public static ListIterator<CValue> CurrentListIterator = null;
	public static CUserObject CurrentIteratingObject;
	public static LinkedList<CChoice> Choices;
	public static LinkedList<CChoice> CurrentChoices;
	public static String ProjectDir = "";
	public static String DesignName = "tubes";
	public static CNull Null = CNull.Create();
	public static CNo No = new CNo();
	public static CCommandText SelectedGadget;
	public static CWindow CurrentWindow;
	public static int Action;
	public static int UpdatingPeriod = 0;
	public static long StartingTime = 0;
	public static int CurrentLineNumber;
	public static int CurrentFileNumber;
	public static boolean UpdateDesign = true;

	public static final int Nothing = 0;
	public static final int MenuAction = 1;
	public static final int MainMenu = 2;

	public static final int NewSave = -2;
	public static final int Back = -1;
	public static final int Continue = 0;
	public static final int NewGame = 1;
	public static final int LoadGame = 2;
	public static final int SaveGame = 3;
	public static final int Quit = 4;
	public static final int Undo = 1;
	
	public enum EExitStatus{ No, Break, Continue, ExitFunction };
	public static EExitStatus ExitStatus;




	public static void Execute() {
		Init();
		CurrentObject = RootObject;
		CurrentContext =RootObject;

		while ( true ) {
			Action = Nothing;
			Choices = new LinkedList<CChoice>();

			CGraphics.WaitToCompleteDrawing();
			if( Actions != null ) Actions.Execute();
			CGraphics.Repaint();
			CGUI.Rearrange( false );
			
			CurrentChoices = Choices;

			Main: while ( true ) {
				if ( Action == MainMenu ) {
					ShowMainMenu();
					break;
				} else if ( CProject.Action == MenuAction ) {
					Action = Nothing;
					if ( SelectedGadget.ID == Undo ) {
						CGraphics.Menus.clear();
						CurrentChoices = Choices;
					} else {
						if ( SelectedGadget.Choice == null ) {
							String CommandText = SelectedGadget.CommandText.toLowerCase();
							for ( CChoice Choice : Choices ) {
								if ( Choice.Name.equals( CommandText ) ) {
									SelectedGadget.Choice = Choice;
									break;
								}
							}
						}

						CChoice Choice = SelectedGadget.Choice;
						if ( Choice != null ) {
							if ( Choice.Children.isEmpty() ) {
								CGraphics.Menus.clear();
								CurrentChoice = Choice;
								CurrentChoice.Command.Parent = CurrentChoice.Object;
								CGraphics.WaitToCompleteDrawing();
								CurrentChoice.Command.Execute();
								CGraphics.Repaint();
								break Main;
							}
							CMenu Menu = new CMenu();
							CurrentChoices = Choice.Children;
							for ( CChoice ChildChoice : Choice.Children ) {
								Menu.AddItem( ChildChoice, 0 );
							}
							Menu.AddItem( "отмена", Undo );
							Menu.X = CGraphics.MouseX;
							Menu.Y = CGraphics.MouseY;
							CGraphics.WaitToCompleteDrawing();
							CGraphics.Menus.add( Menu );
							CGraphics.Repaint();
						}
					}
				}

				if( CProject.RepeatingBlock != null ) {
					CGraphics.WaitToCompleteDrawing();
					CProject.RepeatingBlock.Execute();
					CGraphics.Repaint();
				}
				if( UpdateDesign ) UpdateDesign();
			}
		}
	}
	
	
	
	public static void Init() {
		CurrentObject = RootObject;
		CurrentWindow = CGUI.InformationWindow;
		Actions = GetFunction( "действия&0" );
		RepeatingBlock = GetFunction( "повторяющийсяблок&0" );

		for( CModule Module : RootObject.Initialization ) {
			if( Module.getClass() == CEquate.class ) Module.Execute();
		}
		
		CGraphics.TitleSetter.SetTitle( Name );
		for( CObject Obj : CProject.ObjectsList ) {
			if( Obj != CProject.RootObject ) Obj.Init();
		}
		CProject.StartingTime = System.currentTimeMillis();
		RootObject.Init();
	}



	public static void ShowMainMenu() {
		CMenu Menu = new CMenu();
		Menu.AddItem( "Продолжить", Continue );
		Menu.AddItem( "Новая игра", NewGame );
		Menu.AddItem( "Загрузить игру", LoadGame );
		Menu.AddItem( "Сохранить игру", SaveGame );
		Menu.AddItem( "Выход", Quit );
		Menu.Centerize();
		CGraphics.WaitToCompleteDrawing();
		CGraphics.Menus.add( Menu );
		CGraphics.Repaint();
		Action = Nothing;
		Main: while ( true ) {
			if ( Action == MainMenu ) {
				break;
			} else if ( Action == MenuAction ) {
				Action = Nothing;
				switch( SelectedGadget.ID ) {
					case Continue:
						break Main;
					case NewGame:
						Init();
						break Main;
					case LoadGame:
						Menu = new CMenu();
						if ( !AddSavedGamesToMenu( Menu ) ) break;
						Menu.AddItem( "Назад", Back );
						Menu.Centerize();
						CGraphics.WaitToCompleteDrawing();
						CGraphics.Menus.add( Menu );
						CGraphics.Repaint();
						while ( true ) {
							if ( Action == MainMenu ) {
								break Main;
							} else if ( Action == MenuAction ) {
								if ( SelectedGadget.ID == Back ) {
									CGraphics.WaitToCompleteDrawing();
									CGraphics.Menus.removeLast();
									CGraphics.Repaint();
									break;
								} else {
									LoadGame( SelectedGadget.CommandText );
									break Main;
								}
							}
						}
						break;
					case SaveGame:
						Menu = new CMenu();
						if ( !AddSavedGamesToMenu( Menu ) ) {
							SaveGame( "0.zsg" );
							break Main;
						}
						Menu.AddItem( "Новая запись", NewSave );
						Menu.AddItem( "Назад", Back );
						Menu.Centerize();
						CGraphics.WaitToCompleteDrawing();
						CGraphics.Menus.add( Menu );
						CGraphics.Repaint();
						while ( true ) {
							if ( Action == MainMenu ) {
								break Main;
							} else if ( Action == MenuAction ) {
								if ( SelectedGadget.ID == Back ) {
									CGraphics.WaitToCompleteDrawing();
									CGraphics.Menus.removeLast();
									CGraphics.Repaint();
									break;
								} else if ( SelectedGadget.ID == NewSave ) {
									int Num = 1;
									Num: while( true ) {
										for( CMenuItem MenuItem : Menu.Items ) {
											if ( MenuItem.ID == Num ) {
												Num++;
												continue Num;
											}
										}
										break;
									}
									SaveGame( Num + ".zsg" );
									break Main;
								} else {
									SaveGame( SelectedGadget.CommandText );
									break Main;
								}
							}
						}
						break;
					case Quit:
						System.exit( 0 );
				}
				Action = Nothing;
			}
		}
		Action = Nothing;

		CGraphics.WaitToCompleteDrawing();
		CGraphics.Menus.clear();
		CGraphics.Repaint();
	}



	public static boolean AddSavedGamesToMenu( CMenu Menu ) {
		FileFilter Filter = new FileFilter() {
			public boolean accept( File file ) {
				return file.getName().endsWith( ".zsg" ) && !file.isDirectory();
			}
		};

		File Dir = new File( "." );
		File[] Files = Dir.listFiles( Filter );
		if ( Files.length == 0 ) return false;
		for( File File : Files ) {
			CChoice Choice = new CChoice();
			String Filename = File.getName();
			int Num = Integer.parseInt( Filename.substring( Filename.length() - 4 ) );
			Choice.Name = "Сохраненная игра №" + Num;
			Choice.CommandText = Filename;
			Menu.AddItem( Choice, Num );
		}
		return true;
	}



	public static void LoadGame( String Filename ) {
		/*
		CParser.CurrentReader = new CReader( Filename );
		
		if ( CompareVersions( CParser.CurrentReader.ReadLine(), LastCompatibleSavesVersion ) == -1 ) {
			CGraphics.ShowInfo( "Информация", "Эта сохраненная игра имеет устаревший формат и не может быть загружена" );
			return;
		}

		Clear();
		CStringValue Value = new CStringValue();
		
		while (true) {
			LinkedList<String> Tokens = CParser.ReadTokens();
			if ( Tokens == null ) break;

			CUserObject Obj = ( CUserObject ) CParser.Objects.get( Tokens.getFirst() );
			while (true) {
				Tokens = CParser.ReadTokens();
				if ( Tokens == null ) break;
				CParameter Parameter = ( CParameter ) CParser.Objects.get( Tokens.getFirst() );
				CurrentContext = Obj;
				Value.Value = Tokens.getLast();
				Obj.EquateTo( Value );
			}
		}

		CParser.CurrentReader.Close();

		Init();
		 */
	}



	public static void SaveGame( String Filename ) {
		/*
		PrintWriter PWriter = null;
		try {
			PWriter = new PrintWriter( new FileWriter( Filename ) );
		} catch ( IOException ex ) {
			Logger.getLogger( CProject.class.getName() ).log( Level.SEVERE, null, ex );
		}

		PWriter.println( CProject.Version );
		PWriter.println( Name );
		for( CUserObject Obj : ObjectsList ) {
			PWriter.println( Obj.ID );
			for ( Map.Entry<CParameter,CValue> MapEntry : Obj.Parameters.entrySet() ) {
				PWriter.println( "\t" + MapEntry.getKey().Name + " = " + MapEntry.getValue().ToString() );
			}
			PWriter.println( "" );
			for ( CUserObject Child : Obj.Children ) {
				PWriter.println( "\t" + Child.ID );
			}
			PWriter.println( "" );
		}
		PWriter.println( "" );
		PWriter.close();
		 */
	}



	public static void Clear() {
		for( CUserObject Obj : ObjectsList ) {
			Obj.Parameters.clear();
		}
	}



	public static void ShowGameMenu() {
		while( true ) {
			if ( Action == MenuAction ) {
				Action = Nothing;
				CGraphics.WaitToCompleteDrawing();
				CGraphics.Menus.clear();
				CGraphics.Repaint();
				String Filename = SelectedGadget.Choice.CommandText;
				CParser.Process( Filename );
				ProjectDir = Filename.substring( 0, Filename.lastIndexOf( '/' ) + 1 );
				CProject.Execute();
			}
		}
	}



	public static int CompareVersions( String Version1, String Version2 ) {
		String[] Version1Chunks = Version1.split( "\\." );
		String[] Version2Chunks = Version2.split( "\\." );
		for( int Num = 0; Num < Version1Chunks.length && Num < Version2Chunks.length; Num++ ) {
			int Version1Chunk = Integer.parseInt( Version1Chunks[ Num ] );
			int Version2Chunk = Integer.parseInt( Version2Chunks[ Num ] );
			if ( Version1Chunk > Version2Chunk ) return 1;
			if ( Version1Chunk < Version2Chunk ) return -1;
		}
		if ( Version1Chunks.length > Version2Chunks.length ) return 1;
		if ( Version1Chunks.length < Version2Chunks.length ) return -1;
		return 0;
	}



	public static LinkedList<CChoice> AddCommand( String CommandLine, CCommand Command ) {
		CommandLine = CommandLine.toLowerCase();
		LinkedList<CChoice> ParentChoices = null;
		CChoice Choice;
		for( String MenuitemList : CommandLine.split( "/" ) ) {
			LinkedList<CChoice> NewParentChoices = new LinkedList<CChoice>();
			for ( String MenuItem : MenuitemList.split( ";" ) ) {
				String[] NameAndCommand = MenuItem.split( "\\=" );
				if( NameAndCommand.length > 2 ) CParser.Error( "Несколько знаков равенства в одном пункте меню" );
				if( NameAndCommand.length == 2 ) MenuItem = NameAndCommand[ 1 ];
				
				Choice = null;
				if ( ParentChoices == null ) {
					// поиск пункта выбора с данным именем в корневой директории
					for ( CChoice Choice2 : Choices ) {
						if ( Choice2.CommandText.equals( MenuItem ) ) {
							Choice = Choice2;
							break;
						}
					}
				} else {
					// поиск пункта выбора с данным именем среди вложенных родительского пункта
					for ( CChoice ParentChoice : ParentChoices ) {
						for ( CChoice Choice2 : ParentChoice.Children ) {
							if ( Choice2.CommandText.equals( MenuItem ) ) {
								Choice = Choice2;
								break;
							}
						}
					}
				}

				if ( Choice == null ) {
					// создание нового пункта выбора, если пункт не найден
					Choice = new CChoice();
					Choice.Name = NameAndCommand[ 0 ];
					Choice.CommandText = MenuItem;
					if ( ParentChoices == null ) {
						Choices.add( Choice );
					} else {
						for ( CChoice ParentChoice : ParentChoices ) {
							ParentChoice.Children.add( Choice );
						}
					}
				}
				NewParentChoices.add( Choice );
			}
			ParentChoices = NewParentChoices;
		}
		for ( CChoice ParentChoice : ParentChoices ) {
			ParentChoice.Command = Command;
			ParentChoice.Object = CProject.CurrentObject;
		}
		return ParentChoices;
	}

	
	
	private static CFunctionCall GetFunction( String Name ) {
		CUserFunctionID ID = CParser.Functions.get( Name );
		if( ID != null ) {
			CFunctionCall FunctionCall = new CFunctionCall( ID );
			FunctionCall.Param = new CObject[ 0 ];
			return FunctionCall;
		}
		return null;
	}



	public static void UpdateDesign() {
		if( WhenWindowSizeIsChanged == null ) WhenWindowSizeIsChanged = CProject.GetFunction( "приизмененииразмеровокна&0" );
		if( WhenWindowSizeIsChanged != null ) {
			String OldCurrentDir = ProjectDir;
			ProjectDir = "design/" + DesignName + "/";
			CGraphics.WaitToCompleteDrawing();
			CProject.WhenWindowSizeIsChanged.Execute();
			CGraphics.Repaint();
			ProjectDir = OldCurrentDir;
		}
		CGUI.Rearrange( false );
		UpdateDesign = false;
	}
}
